import { BehaviorTreeFactory } from "./BehaviorTreeFactory";
import { NodeStatus } from "./NodeStatus";
import { ControlNode } from "./controls/ControlNode";
import { DecoratorNode } from "./decorators/DecoratorNode";
import { INode } from "./interfaces/INode";
import { INodeConfig } from "./interfaces/INodeConfig";



export class BehaviorTree<T> {

    root: INode<T> | null = null;

    constructor(data: INodeConfig, blackboard: T) {
        this.root = BehaviorTreeFactory.CreateNode(data, blackboard);
    }

    Tick(dt: number) {
        if (this.root) {
            this.root.Evaluate();
        }
    }

    /**
     * 获取树结构
     */
    Print(): void {
        if (this.root) {
            console.log(BehaviorTree.PrintTreeRecursively(this.root));
        }
    }

    Destroy() {
        if (this.root) {
            this.root.Destroy();
        }
    }

    //-------------------------------------工具方法-------------------------------------

    static ApplyRecursiveVisitor<T>(root: INode<T>, visitor: (node: INode<T>) => void) {
        if (!root) throw new Error("root is null");
        visitor(root);
        if (root instanceof ControlNode) {
            for (let index = 0; index < root.numChildren; index++) {
                const child = root.GetChild(index);
                this.ApplyRecursiveVisitor(child, visitor);
            }
        } else if (root instanceof DecoratorNode) {
            this.ApplyRecursiveVisitor(root.GetChild(), visitor);
        }
    }

    static PrintTreeRecursively<T>(root: INode<T>): string {
        const endl = '\n';
        let stream = '';
        let recursivePrint = (indent: number, node: INode<T>) => {
            for (let i = 0; i < indent; i++) {
                stream += '   ';
            }
            if (!node) {
                stream += '!null!' + endl;
                return;
            }
            let status = '';
            switch (node.status) {
                case NodeStatus.FAILURE:
                    status = 'failure';
                    break;
                case NodeStatus.RUNNING:
                    status = 'running';
                    break;
                case NodeStatus.SUCCESS:
                    status = 'success';
                    break;
            }
            stream += "[" + status + "]" + node.name + endl
            indent++;

            if (node instanceof ControlNode) {
                for (let index = 0; index < node.numChildren; index++) {
                    const child = node.GetChild(index);
                    recursivePrint(indent, child);
                }
            } else if (node instanceof DecoratorNode) {
                recursivePrint(indent, node.GetChild());
            }
        };

        stream += '----------------' + endl;
        recursivePrint(0, root);
        stream += '----------------' + endl;
        return stream;
    }
}